其他
坏了,Glide的超时居然失控了!这可如何是好
本文作者
作者:newki
链接:
https://juejin.cn/post/7265332526544814119
本文由作者授权发布。
前言
调用接口从网络拉取用户头像,目前数据量不大,大致1000多个人。(用了自定义队列) 使用 Glide 下载头像到本地沙盒 File (为了方便的缓存下次更快)。 识别头像中的人脸信息,并生成人脸Bitmap,(本身有成功失败的处理与重试机制) 生成人脸对应的特征,并保存人脸特征数据和人脸特征图片到沙盒 File 。 封装人脸对象并加载到内存中保持全局单例。 场景业务:与Camera的预览画面中获取到的活体人脸进行人脸比对。
问题嘛,就是图片加载的问题,有些图片无法加载,有些图片太大加载时间过长,有些根本就不是图片,有些网络慢,不稳定,或者干脆就无网,有些是访问权限问题,为了让图片下载队列能正常运转加入了 Glide 的超时机制,踩坑之路由此展开。
implementation 'com.github.bumptech.glide:glide:4.15.1'
implementation 'com.github.bumptech.glide:annotations:4.15.1'
kapt 'com.github.bumptech.glide:compiler:4.15.1'
fun Any.extDownloadImage(context: Context?, path: Any?, block: (file: File) -> Unit) {
var startMillis = 0L
var endMillis = 0L
GlideApp.with(context!!)
.load(path)
.timeout(15000) // 15秒
.downloadOnly(object : SimpleTarget<File?>() {
override fun onLoadStarted(placeholder: Drawable?) {
startMillis = System.currentTimeMillis()
YYLogUtils.w("开始加载:$startMillis")
super.onLoadStarted(placeholder)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗时:${endMillis - startMillis}")
super.onLoadFailed(errorDrawable)
}
override fun onResourceReady(resource: File, transition: Transition<in File?>?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗时:${endMillis - startMillis}")
block(resource)
}
})
}
val url = "https://s3.ap-southeast-1.amazonaws.com/yycircle-ap/202307/11/KZ8xIVsrlrYtjhw3t2t2RTUj0ZTWUFr2EhawOd4I-810x1080.jpeg"
extDownloadImage(this@MainActivity, url, block = { file ->
YYLogUtils.w("file:${file.absolutePath}")
})
1.1 HttpURLConnection 没网的情况
com.bumptech.glide.load.HttpException: Failed to connect or obtain data, status code: -1
1.2 HttpURLConnection 有网的但是不通
class com.bumptech.glide.load.HttpException: Failed to connect or obtain data, status code: -1
class HttpTest {
private final HttpUrlConnectionFactory connectionFactory = new DefaultHttpUrlConnectionFactory();
public HttpTest() {
}
public HttpURLConnection buildAndConfigureConnection(URL url, Map<String, String> headers) throws HttpException {
HttpURLConnection urlConnection;
try {
urlConnection = connectionFactory.build(url);
} catch (IOException e) {
throw new RuntimeException("URL.openConnection threw");
}
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(7000);
urlConnection.setReadTimeout(7000);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);
return urlConnection;
}
interface HttpUrlConnectionFactory {
HttpURLConnection build(URL url) throws IOException;
}
private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
DefaultHttpUrlConnectionFactory() {}
@Override
public HttpURLConnection build(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
}
}
java.net.UnknownHostException: Unable to resolve host "s3.ap-southeast-1.amazonaws.com": No address associated with hostname
1.3 HttpURLConnection 有网通了,但是没访问权限
GlideApp.with(context!!)
.load(path)
.apply(options)
.timeout(15000)
.into(object : SimpleTarget<Drawable>() {
override fun onLoadStarted(placeholder: Drawable?) {
startMillis = System.currentTimeMillis()
YYLogUtils.w("开始加载:$startMillis")
super.onLoadStarted(placeholder)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗时:${endMillis - startMillis}")
super.onLoadFailed(errorDrawable)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗时:${endMillis - startMillis}")
block(resource)
}
})
1.4 换成 OkHttp3
implementation 'com.github.bumptech.glide:okhttp3-integration:4.15.1'
GlideApp.with(context!!)
.load(path)
.apply(options)
.timeout(20000)
.into(object : SimpleTarget<Drawable>() {
override fun onLoadStarted(placeholder: Drawable?) {
startMillis = System.currentTimeMillis()
YYLogUtils.w("开始加载:$startMillis")
super.onLoadStarted(placeholder)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onLoadFailed-Drawable,一共耗时:${endMillis - startMillis}")
super.onLoadFailed(errorDrawable)
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
endMillis = System.currentTimeMillis()
YYLogUtils.w("Glide-onResourceReady-Drawable,一共耗时:${endMillis - startMillis}")
block(resource)
}
})
哎,真的是头都大了,不是说好的开箱即用吗,咋个这么多问题,还分这么多情况,真不知道该如何是好。
@GlideModule
public final class HttpGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
// 替换自定义的Glide网络加载
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(GlideOkHttpUtils.getHttpClient()));
}
}
public class GlideOkHttpUtils {
public static OkHttpClient getHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor()) //打印请求日志,可有可无
.sslSocketFactory(getSSLSocketFactory())
.hostnameVerifier(getHostnameVerifier());
return builder.build();
}
/**
* getSSLSocketFactory、getTrustManagers、getHostnameVerifier
* 使OkHttpClient支持自签名证书,避免Glide加载不了Https图片
*/
private static SSLSocketFactory getSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static TrustManager[] getTrustManagers() {
return new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}};
}
private static HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
}
}
想设置几秒就是几秒,没有重试导致时间不对一说。这确实是一种方案。
suspend fun Any.downloadImageWithGlide(imgUrl: String): File {
return suspendCancellableCoroutine { cancellableContinuation ->
GlideApp.with(commContext())
.load(imgUrl)
.timeout(15000) //设不设都一样,反正不靠你
.diskCacheStrategy(DiskCacheStrategy.DATA)
.downloadOnly(object : SimpleTarget<File?>() {
override fun onResourceReady(resource: File, transition: Transition<in File?>?) {
cancellableContinuation.resume(resource)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
cancellableContinuation.resumeWithException(RuntimeException("加载失败了"))
}
})
}
}
launch{
...
try {
val file = withTimeout(15000) {
downloadImageWithGlide(userInfo.avatarUrl)
}
YYLogUtils.e("注册人脸服务-图片加载成功:${file.absolutePath}")
//下载成功之后赋值本地路径到对象中
userInfo.avatarPath = file.absolutePath
//去注册人脸
registerHotelMember(userInfo)
} catch (e: TimeoutCancellationException) {
YYLogUtils.e("注册人脸服务-图片加载超时:${e.message}")
checkImageDownloadError(userInfo)
} catch (e: Exception) {
YYLogUtils.e("注册人脸服务-图片加载错误:${e.message}")
checkImageDownloadError(userInfo)
}
}
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!